#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <stdarg.h>
#include <errno.h>
#include "logger.h"

/* 日志记录器结构体 */
typedef struct
{
	FILE *fp;
	pthread_mutex_t mutex;
	LoggerConfig config;
	int curr_size;
} Logger;

static Logger logger;	/* 全局日志实例 */
const char *lelvel_string[] = {"LOG_DEBUG", "LOG_INFO", "LOG_WARN", "LOG_ERROR", "LOG_FILE"};

static void log_rotate()
{
	if (logger.curr_size >= logger.config.max_size)
	{
		fclose(logger.fp);
		char backup[512];
		time_t now = time(NULL);
		struct tm *t = localtime(&now);
		memset(backup, 0x00, sizeof(backup));
		snprintf(backup, sizeof(backup), "%s.%02d%02d%02d", logger.config.filename, t->tm_hour, t->tm_min, t->tm_sec);
		rename(logger.config.filename, backup);
		logger.fp = fopen(logger.config.filename, "a+");
		if (logger.fp == NULL) return;
		logger.curr_size = 0;
	}
}

int log_init(const LoggerConfig *config)
{
	memset(&logger, 0x00, sizeof(Logger));

	/* 初始化日志配置 */
	logger.config.level = config->level;
	strncpy(logger.config.filename, config->filename, sizeof(logger.config.filename) - 1);
	logger.config.max_size = config->max_size * 1024 * 1024;
	logger.config.isprint = config->isprint;

	/* 创建日志目录 */
	char logdir[128];
	memset(logdir, 0x00, sizeof(logdir));
	const char *base_name = strrchr(config->filename, '/');
	size_t len = base_name - config->filename;
	strncpy(logdir, config->filename, len);
	if (mkdir_p(logdir))
	{
		fprintf(stderr, "创建目录[%s]失败!\n", logdir);
	}

	/* 打开日志文件 */
	logger.fp = fopen(logger.config.filename, "a+");
	if (!logger.fp)
	{
		fprintf(stderr, "打开日志文件失败:[%s]\n", logger.config.filename);
		return -1;
	}

	/* 初始化互斥锁 */
	if (pthread_mutex_init(&logger.mutex, NULL) != 0)
	{
		fclose(logger.fp);
		logger.fp = NULL;
		return -1;
	}

	/* 获取当前日志文件大小 */
	fseek(logger.fp, 0, SEEK_END);
	logger.curr_size = ftell(logger.fp);
	rewind(logger.fp);

	return 0;
}

int log_write(LogLevel level, const char *file, int line, const char *format, ...)
{
	if (level < logger.config.level) return 0;
	if (!logger.fp) return -1;  /* 检查文件指针 */

	pthread_mutex_lock(&logger.mutex);
	struct timeval tv;
	gettimeofday(&tv, NULL);
	struct tm tm;
	localtime_r(&tv.tv_sec, &tm);

	/* 获取日志文件名(不含路径) */
	const char *base_name = strrchr(file, '/');
	base_name = base_name ? base_name + 1 : file;

	/* 格式化日志头 */
	char header[128];
	memset(header, 0, sizeof(header));
	int headr_len = snprintf(header, sizeof(header), "%04d-%02d-%02d %02d:%02d:%02d.%03ld|%8d|%s|%s:%d|", 
			tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec / 1000,
			getpid(), lelvel_string[level], base_name, line);

	/* 处理可变参数 */
	va_list args;
	va_start(args, format);
	/* 创建副本用于计算长度 */
	va_list args_copy;
	va_copy(args_copy, args);
	/* 创建副本用于动态计算实际字符串长度 */
	int required = vsnprintf(NULL, 0, format, args_copy);
	va_end(args_copy);	/* 结束副本 */
	char *message = malloc(required + 1);	/* 分配内存 */
	if (message == NULL) { va_end(args); return -1; }
	int msg_len = vsnprintf(message, required + 1, format, args);
	va_end(args);

	/* 写日志 */
	if (logger.config.isprint != 0 && level != LOG_FILE) { fprintf(stdout, "%s\n", message); }
	fprintf(logger.fp, "%s%s\n", header, message);
	fflush(logger.fp);
	free(message);

	/* 更新日志文件大小并检查轮转 */
	logger.curr_size += headr_len + msg_len + 2;
	log_rotate();

	pthread_mutex_unlock(&logger.mutex);
	return 0;
}

int log_close()
{

	if (logger.fp)
	{
		fclose(logger.fp);
		pthread_mutex_destroy(&logger.mutex);
	}
	return 0;
}

/**
 * @brief 递归创建目录路径（类似 mkdir -p 命令）
 *
 * 本函数递归创建完整的目录路径，包括所有中间目录。如果目录已存在，不会视为错误。
 * 函数会创建输入路径的所有必要父目录，最终创建目标目录。权限固定为 0755 (rwxr-xr-x)。
 *
 * @param path 要创建的目录路径（绝对或相对路径）
 *
 * @return 执行状态
 *   @retval  0  成功创建所有目录（或目录已存在）
 *   @retval -1  创建失败（内存不足/权限不足等）
 *
 * @note 重要注意事项：
 *  1. 已存在的目录不会报错（EEXIST 错误会被忽略）
 *  2. 权限固定为 0755（rwxr-xr-x）
 *  3. 路径分隔符必须为 '/'（POSIX 标准）
 *  4. 内存分配失败或创建目录失败（非 EEXIST）会返回错误
 *  5. 函数会修改路径字符串的临时副本，但不修改原始输入
 *
 * @example 示例：
 *  mkdir_p("/tmp/a/b/c")    // 创建多级目录
 *  mkdir_p("/tmp/existing") // 处理已存在目录
 */
int mkdir_p(const char *path)
{
	// 尝试直接创建目标目录
	if ( mkdir(path, 0755) == 0 ) return 0;

	// 复制路径（避免修改原始字符串）
	char *tmp = strdup(path);
	if ( !tmp ) return -1;

	char *dir = tmp;
	char *sep = dir;

	// 逐级创建目录
	while ( (sep = strchr(sep, '/')) )
	{
		*sep = '\0';
		if ( *dir != '\0' && mkdir(dir, 0755) && errno != EEXIST ) { free(tmp); return 0; }
		*sep++ = '/';
	}

	// 创建最后一级目录
	if ( mkdir(dir, 0755) && errno != EEXIST ) { free(tmp); return 0; }

	return 0;
}
